import React, { useReducer, createContext } from 'react';
import get from 'lodash/get';
import findIndex from 'lodash/findIndex';
import find from 'lodash/find';
import set from 'lodash/set';
import { mapNodes, hasChildren, getUniqueID, filterNodes } from '@/utils/utils';
import { setValue, updateNodeRules, getValue } from './utils';
import { ARRAY_TYPE, OBJECT_TYPE } from './settings';
import { getDefaultNode } from '@/pages/Validator/utils';

// 1. 创建全局的Context
const Context = createContext();

export default Context;

// 2. 创建全局的Reducer
const initState = {
  data: null,
  globalToggle: { timestamp: +new Date() - 1000 * 2, status: true }, // 是否展开全部子节点
};

function reducer(state, action) {
  const { type, payload } = action;

  if (type === 'set_the_whole_tree') {
    return {
      ...state,
      ...payload,
    };
  }
  if (type === 'open_or_close_node') {
    return {
      ...state,
      data: mapNodes([state.data], k => {
        const a = set({}, 'expanded', payload);
        const s = k.state || {};
        k.state = { ...s, ...a };
        return k;
      })[0],
      globalToggle: { timestamp: +new Date(), status: payload },
    };
  }

  // 所有节点设置必填
  if (type === 'toggle_required') {
    const { checked, nodePath } = payload;

    const toggleAll = nodePath === '';

    let newData;

    if (toggleAll) {
      // 全量
      const d = mapNodes([state.data], node => {
        const newRules = updateNodeRules(node, 'required', checked);

        return { ...node, rules: newRules };
      });

      // eslint-disable-next-line prefer-destructuring
      newData = d[0];
    } else {
      // 某个节点
      const node = get(state.data, nodePath);

      const newRules = updateNodeRules(node, 'required', checked);

      newData = setValue(state.data, nodePath, { ...node, rules: newRules });
    }

    return {
      ...state,
      data: newData,
    };
  }

  // 更新节点nodeType,适用于除`rules`外的普通属性。
  if (type === 'update_node_normal_attr') {
    const { value, path: nodeAttrPath, attrName } = payload;

    let newData = setValue(state.data, nodeAttrPath, value);

    const a = nodeAttrPath.split('.');
    a.pop();
    const nodePath = a.join('.');
    const node = getValue(newData, nodePath);
    // 当nodeType切换时，需要额外处理children属性。只允许`object` 和 `array`有子节点
    if (attrName === 'nodeType') {
      if (![OBJECT_TYPE, ARRAY_TYPE].includes(value)) {
        delete node.children;

        newData = setValue(state.data, nodePath, node);
      } else {
        // 主动追加子节点，避免再手动点

        const c = [
          {
            ...getDefaultNode(node),
            nodeName: value === ARRAY_TYPE ? 'items' : getUniqueID('field'),
            state: { expanded: true },
          },
        ];

        newData = setValue(state.data, nodePath, {
          ...node,
          state: { expanded: true },
          children: c,
        });
      }
    }

    return {
      ...state,
      data: newData,
    };
  }

  if (type === 'update_node_rules_attr') {
    const { nodePath, current, rules } = payload;

    let newRules;
    const oldRules = get(current, 'rules');

    if (Array.isArray(oldRules)) {
      const r = find(oldRules, { type: 'required' });
      // 如果存在required，不能被重置
      if (r) {
        newRules = [r, ...rules];
      }
    }

    newRules = rules;
    const newData = setValue(state.data, nodePath, { ...current, rules: newRules });

    return {
      ...state,
      data: newData,
    };
  }

  // 新增兄弟节点(操作父节点的children)
  if (type === 'add_sibling_node') {
    const { parent = {}, current, nodePath } = payload;

    const a = nodePath.split('.');
    a.pop();

    const parentNodePath = a.join('.');

    if (Array.isArray(get(parent, 'children'))) {
      const index = findIndex(parent.children, { id: current.id });
      // 紧挨着当前节点的下一个位置
      parent.children.splice(index + 1, 0, getDefaultNode(parent));
    } else {
      parent.children = [getDefaultNode(parent)];
    }
    const newData = setValue(state.data, parentNodePath, parent);

    return {
      ...state,
      data: newData,
    };
  }

  // 新增子节点（操作当前节点的children）
  if (type === 'add_child_node') {
    const { current, nodePath } = payload;

    const defaultNode = getDefaultNode(current);

    if (Array.isArray(get(current, 'children'))) {
      current.children.push(defaultNode);
    } else {
      current.children = [defaultNode];
    }

    const newData = setValue(state.data, nodePath, current);

    return {
      ...state,
      data: newData,
    };
  }

  // 删除当前节点
  if (type === 'delete_current_node') {
    const { nodePath } = payload;

    if (nodePath === '') {
      return {
        ...state,
        data: null,
      };
    }

    const newData = setValue(state.data, nodePath, null);

    const c = filterNodes(newData.children, node => node !== null);

    return {
      ...state,
      data: { ...newData, children: c },
    };
  }

  // 复制当前节点（操作当前节点的children）
  if (type === 'copy_current_node') {
    const { parent, current, nodePath } = payload;

    const a = nodePath.split('.');
    a.pop();

    const parentNodePath = a.join('.');

    const copyOne = { ...current, nodeName: getUniqueID('Copy'), id: getUniqueID('_id') };

    if (hasChildren(copyOne)) {
      copyOne.children = mapNodes(copyOne.children, k => {
        k.id = getUniqueID('_id');
        return k;
      });
    }

    if (Array.isArray(get(parent, 'children'))) {
      const index = findIndex(parent.children, { id: current.id });
      // 紧挨着当前节点的下一个位置
      parent.children.splice(index + 1, 0, copyOne);
    } else {
      parent.children = [copyOne];
    }

    const newData = setValue(state.data, parentNodePath, parent);

    return {
      ...state,
      data: newData,
    };
  }

  // 修改节点名称
  if (type === 'modify_node_name') {
    const { node, nodePath, newName } = payload;
    const _newData = mapNodes([node], k => {
      const parentPathLength = node.nodePath.length;

      const newNodePath = k.nodePath.map((each, index) =>
        index === parentPathLength - 1 ? newName : each
      );

      if (k.id === node.id) {
        return {
          ...k,
          nodeName: newName,
          nodePath: newNodePath,
        };
      }

      return {
        ...k,
        nodePath: newNodePath,
      };
    });


    const newData = setValue(state.data, nodePath, _newData[0]);

    return {
      ...state,
      data: newData
    };
  }

  throw new Error();
}

// 3. 将全局useReducer返回的state和dispatch传递给全局Context.Provider的value中
export const Provider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, initState);

  return <Context.Provider value={{ state, dispatch }}>{children}</Context.Provider>;
};
